package com.ruoyi.system.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.core.utils.uuid.UUID;
import com.ruoyi.system.config.SysRedisManegeHelper;
import com.ruoyi.system.domain.RedisDataInfo;
import com.ruoyi.system.domain.RedisInfo;
import com.ruoyi.system.domain.RedisVo;
import com.ruoyi.system.mapper.SysRedisMapper;
import com.ruoyi.system.service.ISysRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @author wjialong
 */
@Service
public class SysRedisServiceImpl implements ISysRedisService {

    @Autowired
    SysRedisMapper redisMapper;

    @Autowired
    ObjectMapper objectMapper;

    Map<String, StringRedisTemplate> redisMap = SysRedisManegeHelper.getRedisMap();

    /**
     * 获取数据库中的redis列表
     *
     * @return
     */
    @Override
    public List<RedisInfo> getRedisList() {
        return redisMapper.getRedisList();
    }

    @Override
    public List<RedisVo> iniRedisList() {
        List<RedisInfo> redisList = getRedisList();
        List<RedisVo> vos = new ArrayList<>();
        for (RedisInfo nowRedisInfo : redisList) {
            RedisVo vo = new RedisVo();
            vo.setTitle(nowRedisInfo.getName());
            vo.setKey(nowRedisInfo.getId());
            vo.setIsLeaf(false);
            vos.add(vo);
        }

        return vos;
    }

    /**
     * 新增redisInfo
     *
     * @param redisInfo
     * @return
     */
    @Override
    public int addRedis(RedisInfo redisInfo) {
        redisInfo.setId(UUID.fastUUID().toString());
        return redisMapper.addRedis(redisInfo);
    }

    @Override
    public RedisInfo getRedisInfo(String id) {
        return redisMapper.getRedisInfo(id);
    }

    /**
     * 更新数据
     */
    @Override
    public void updateRedis(RedisInfo redisInfo) {
        redisMapper.updateRedis(redisInfo);
    }

    /**
     * 删除数据
     */
    @Override
    public void deleteRedis(String id) {
        redisMapper.deleteRedis(id);
    }

    /**
     * 根据ip:port获取redis的连接工厂
     *
     * @param redisInfo
     * @return
     */
    @Override
    public LettuceConnectionFactory getNewFactory(RedisInfo redisInfo) {
        RedisStandaloneConfiguration configuration =
                new RedisStandaloneConfiguration(redisInfo.getHost(), redisInfo.getPort());
        if (redisInfo.getPassword() != null) {
            configuration.setPassword(redisInfo.getPassword());
        }
        LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration);
        factory.afterPropertiesSet();
        return factory;
    }

    /**
     * 获取dbList
     *
     * @param redisInfo
     * @return
     */
    @Override
    public List<RedisVo> getDBList(RedisInfo redisInfo) {
        StringRedisTemplate stringRedisTemplate = getRedisTemplate(redisInfo, 0);
        RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();
        // 获取redis服务器所启用的db数量
        Properties databases = connection.getConfig("databases");
        int dbCount = Integer.parseInt((String) databases.get("databases"));
        // 获取redis服务器每个db的key的数量
        Properties keyspace = connection.info("keyspace");
        List<RedisVo> vos = new ArrayList<>();
        // 遍历所有db
        for (int i = 0; i < dbCount; i++) {
            RedisVo vo = new RedisVo();
            String dbName = "db" + String.valueOf(i);
            String dbSize = (String) keyspace.get(dbName);
            dbSize = dbSize == null ? "0" : dbSize.substring(dbSize.indexOf("=") + 1, dbSize.indexOf(","));

            // 设置前端所需的返回格式
            vo.setTitle(dbName + "(" + dbSize + ")");
            vo.setKey(redisInfo.getId() + "-" + i);
            vo.setIsLeaf(true);

            vos.add(vo);
        }
        connection.close();
        return vos;
    }

    /**
     * 获取StringRedisTemplate操作对象
     *
     * @param redisDataInfo 设置 redisID,dbID 即可
     * @return
     */
    @Override
    public StringRedisTemplate getRedisTemplate(RedisDataInfo redisDataInfo) {
        RedisInfo redisInfo = getRedisInfo(redisDataInfo.getRedisId());
        return getRedisTemplate(redisInfo, redisDataInfo.getDbId());
    }

    @Override
    public StringRedisTemplate getRedisTemplate(RedisInfo redisInfo, Integer dbId) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(redisInfo.getHost());
        stringBuilder.append(":");
        stringBuilder.append(redisInfo.getPort());
        stringBuilder.append(":");
        stringBuilder.append(dbId);
        stringBuilder.append(":");
        stringBuilder.append(redisInfo.getPassword());
        String addr = stringBuilder.toString();

        StringRedisTemplate stringRedisTemplate = redisMap.get(addr);
        if (stringRedisTemplate == null) {
            // 获取连接工厂
            LettuceConnectionFactory factory = getNewFactory(redisInfo);
            //  选择所连接的数据库
            factory.setDatabase(dbId);
            factory.afterPropertiesSet();
            stringRedisTemplate = new StringRedisTemplate();
            stringRedisTemplate.setConnectionFactory(factory);
            stringRedisTemplate.afterPropertiesSet();
            if (testConnect(stringRedisTemplate)) {
                redisMap.put(addr, stringRedisTemplate);
            }
        }

        return stringRedisTemplate;
    }

    public boolean testConnect(StringRedisTemplate redisTemplate) {
        boolean connectAble = false;
        if (redisTemplate != null) {
            try {
                RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
                connection.close();
                connectAble = true;
            } catch (Exception ignored) {
            }
        }
        return connectAble;
    }
}
